﻿/**************************************************************
 *
 * 唯一标识：a090197a-e0b5-409b-bc64-fe8154bd115f
 * 命名空间：Microsoft.Extensions.DependencyInjection
 * 创建时间：2024/6/18 20:50:46
 * 机器名称：DESKTOP-HJ4OAG9
 * 创建者：CocoYuan
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：
 *
 **************************************************************/

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Sgr;
using Sgr.Application;
using Sgr.Application.Queries;
using Sgr.Data;
using Sgr.Domain.Uow;
using Sgr.EntityFrameworkCore;
using Sgr.Exceptions;

namespace Microsoft.Extensions.DependencyInjection
{
    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection AddSgrEntityFrameworkCore(this IServiceCollection services)
        {
            services.AddSgrData();
            services.TryAddSingleton<IDbContextConfiguratorFactory, DefaultDbContextConfiguratorFactory>();

            //services.AddScoped<ISgrUnitOfWork>(sp => { return sp.GetRequiredService<SgrDbContext>(); });

            //注入查询服务的ISgrQueryContext
            if (services.Any(f => f.ServiceType == typeof(ISgrQueryContext)))
                services.Replace(ServiceDescriptor.Scoped<ISgrQueryContext, SgrQueryContext>());
            else
                services.AddScoped<ISgrQueryContext, SgrQueryContext>();

            //注入工作单元的IDbContextProvider
            services.TryAddTransient(typeof(IDbContextProvider<>), typeof(UnitOfWorkDbContextProvider<>));

            services.TryAddEnumerable(ServiceDescriptor.Singleton<IExceptionHandler, EfDbExceptionHandler>());

            return services;
        }

        /// <summary>
        /// 添加 Sgr 数据库上下文服务，支持指定主/只读 DbContext 类型，并允许传入自定义配置回调。
        /// </summary>
        public static IServiceCollection AddSgrDbContext<TDbContext>(
            this IServiceCollection services,
            Action<IServiceProvider, DbContextOptionsBuilder>? configurator = null)
            where TDbContext : DbContext
        {
            // 采用传入的配置函数或默认内部实现
            Action<IServiceProvider, DbContextOptionsBuilder> cfg = configurator ?? ConfiguratorDbContextDefault;

            // 配置主数据库上下文
            services.AddDbContext<TDbContext>((sp, builder) =>
            {
                cfg(sp, builder);
            });

            return services;
        }

        /// <summary>
        /// 添加 Sgr 数据库上下文服务，支持指定主/只读 DbContext 类型，并允许传入自定义配置回调。
        /// </summary>
        public static IServiceCollection AddSgrDbContext<TDbContext, TReadOnlyDbContext>(
            this IServiceCollection services,
            Action<IServiceProvider, DbContextOptionsBuilder, bool>? configurator = null)
            where TDbContext : DbContext
            where TReadOnlyDbContext : DbContext
        {
            // 采用传入的配置函数或默认内部实现
            Action<IServiceProvider, DbContextOptionsBuilder, bool> cfg = configurator ?? ConfiguratorDbContext;

            // 因为使用了 IRequestRouteContext 来决定是否强制走主库，因此此处暂时不支出使用 AddDbContextPool

            // 配置主数据库上下文
            services.AddDbContext<TDbContext>((sp, builder) =>
            {
                cfg(sp, builder, false);
            });

            // 配置只读数据库上下文（用于从库）
            services.AddDbContext<TReadOnlyDbContext>((sp, builder) =>
            {
                cfg(sp, builder, true);
            });

            return services;
        }

        /// <summary>
        /// 库内部默认的 DbContext 配置实现（保持原有行为）
        /// </summary>
        private static void ConfiguratorDbContextDefault(IServiceProvider sp, DbContextOptionsBuilder builder)
        {
            ConfiguratorDbContext(sp, builder, false);
        }

        /// <summary>
        /// 库内部默认的 DbContext 配置实现（保持原有行为）
        /// </summary>
        private static void ConfiguratorDbContext(IServiceProvider sp, DbContextOptionsBuilder builder, bool isSlave)
        {
            // 获取数据库连接字符串管理器和配置器工厂
            IDataConnectionStringManager dataConnectionStringManager = sp.GetRequiredService<IDataConnectionStringManager>();
            IDbContextConfiguratorFactory configuratorFactory = sp.GetRequiredService<IDbContextConfiguratorFactory>();

            // 获取数据库连接字符串配置（空字符串表示使用默认数据库）
            DataConnectionString dataConnectionString = dataConnectionStringManager.GetDataConnectionString("");

            // 创建对应数据库类型的配置器
            IDbContextConfigurator configurator = configuratorFactory.CreateConfigurator(dataConnectionString.DbProvider);

            // 如果是从库并且没有强制使用主库，则获取从库连接字符串；否则使用主库连接字符串
            string connectionString = isSlave ?
                GetSlaveConnectionString(sp, dataConnectionString) : dataConnectionString.ConnectionString;

            // 配置数据库连接
            configurator.Configure(builder, connectionString);

            // 配置日志和错误处理
            builder.ConfigureLoggingAndErrors(sp);
        }

        /// <summary>
        /// 获取从库连接字符串（委托给 ISlaveDataConnectionStringResolve）
        /// </summary>
        private static string GetSlaveConnectionString(IServiceProvider sp, DataConnectionString dataConnectionString)
        {
            // 可选策略：当 IRequestRouteContext 不存在时，默认使用主库（避免 design-time/migration 指向从库）
            IRequestRouteContext? requestRouteContext = sp.GetService<IRequestRouteContext>();
            if (requestRouteContext == null)
            {
                // 记录日志或选择策略：这里选择返回主库连接字符串
                return dataConnectionString.ConnectionString;
            }

            if (requestRouteContext.ForceUseMaster)
                return dataConnectionString.ConnectionString;

            ISlaveDataConnectionStringResolve slaveDataConnectionStringResolve = sp.GetRequiredService<ISlaveDataConnectionStringResolve>();
            return slaveDataConnectionStringResolve.GetSlaveDatabaseConnectionString(dataConnectionString);
        }
    }
}